package com.kcsoft.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kcsoft.config.ChatConfig;
import com.kcsoft.mapper.ChatMapper;
import com.kcsoft.mapper.CommandMapper;
import com.kcsoft.pojo.Assistant;
import com.kcsoft.pojo.Command;
import com.kcsoft.pojo.Result;
import com.kcsoft.pojo.Sessions;
import com.kcsoft.service.ChatService;
import com.kcsoft.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.net.URI;
import java.net.URLClassLoader;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ChatServiceImpl implements ChatService {

    @Autowired
    ChatConfig chatConfig;

    @Autowired
    ChatMapper chatMapper;

    @Autowired
    CommandMapper commandMapper;

    @Value("${ragflow.url}")
    private String url;

    @Value("${ragflow.api_key}")
    private String apiKey;

    @Value("${ragflow.chat_id}")
    private String chatId;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String genSession(String chatName, String assistantId, String type) {
        String realUrl = url;
        if (type.equals("ASSISTANT"))
            realUrl += "chats/";
        if (type.equals("AGENT"))
            realUrl += "agents/";
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/json");
        headers.set("Authorization", "Bearer " + apiKey);
        Map<String, String> data = new HashMap<>();
        data.put("name", chatName);

        HttpEntity<Map<String, String>> request = new HttpEntity<>(data, headers);
        ResponseEntity<String> response = chatConfig.restTemplate().exchange(
                realUrl + assistantId +  "/sessions",
                HttpMethod.POST,
                request,
                String.class);
        String responseBody = response.getBody();
        if (responseBody != null){
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                JsonNode rootNode = objectMapper.readTree(responseBody);
                JsonNode dataNode = rootNode.path("data");
                if (dataNode.isObject()){
                    return dataNode.path("id").asText();
                }
            }catch (JsonProcessingException e){
                return "";
            }
        }
        return "";
    }

    @Override
    public Flux<String> getAnswer(String sessionId, String question, String assistantId) {
        String realUrl = url;
        Sessions session = chatMapper.findBySession(sessionId);
        if (session.getType().equals("ASSISTANT"))
            realUrl += "chats/";
        if (session.getType().equals("AGENT"))
            realUrl += "agents/";
        String url = realUrl + assistantId + "/completions";

        Map<String, Object> data = new HashMap<>();
        data.put("session_id", sessionId);
        data.put("question", question);
        data.put("stream", true);

        Command command = commandMapper.findByCommand(question);
        if (command != null){
            commandMapper.updateTotal(question);
        }

        Flux<String> response = chatConfig.webClient().post()
                .uri(url)
                .header("Authorization", "Bearer " + apiKey)
                .header("Content-Type", "application/json")
                .bodyValue(data)
                .retrieve()
                .bodyToFlux(String.class)
                .map(s -> s + "\n\n\n\n");
        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSession(String sessionId, String chatName, String assistantId, String type) {
        Map<String, Object> claims = ThreadLocalUtil.get();
        Integer userId = (Integer) claims.get("id");
        System.out.println(userId);
        chatMapper.saveSession(sessionId, chatName, userId, assistantId, type);
    }

    @Override
    public String getHistory(String sessionId) {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url + chatId + "/sessions?id=" + sessionId))
                .header("Authorization", "Bearer " + apiKey)
                .GET()
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            return response.body();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSession(String sessionId) {
        String realUrl = url;
        Sessions session = chatMapper.findBySession(sessionId);
        chatMapper.deleteSession(sessionId);
        if (session.getType().equals("ASSISTANT"))
            realUrl += "chats/" + session.getAssistantId() + "/sessions";
        if (session.getType().equals("AGENT"))
            realUrl += "agents/" + session.getAssistantId() + "/sessions";
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/json");
        headers.set("Authorization", "Bearer " + apiKey);
        Map<String, List<String>> data = new HashMap<>();
        data.put("ids", List.of(sessionId));

        HttpEntity<Map<String, List<String>>> request = new HttpEntity<>(data, headers);
        ResponseEntity<String> response = chatConfig.restTemplate().exchange(
                realUrl,
                HttpMethod.POST,
                request,
                String.class);
    }

    @Override
    public Result<List<Object>> getAssistants() {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + apiKey);
        HttpEntity<Map<String, String>> request = new HttpEntity<>(headers);

        ResponseEntity<String> responseAss = chatConfig.restTemplate().exchange(
                url + "chats",
                HttpMethod.GET,
                request,
                String.class);
        ResponseEntity<String> responseAgent = chatConfig.restTemplate().exchange(
                url + "agents",
                HttpMethod.GET,
                request,
                String.class);
        String jsonAss = responseAss.getBody();
        String jsonAgent = responseAgent.getBody();
        ObjectMapper objectMapper = new ObjectMapper();
        List<Object> resList = new ArrayList<>();
        try {
            JsonNode rootNodeAss = objectMapper.readTree(jsonAss);
            JsonNode dataArr = rootNodeAss.path("data");
            for (JsonNode node : dataArr) {
                Map<String, String> resMap = new HashMap<>();
                 String id = node.path("id").asText();
                 String name = node.path("name").asText();
                 String description = node.path("description").asText();
                 String type = "ASSISTANT";
                 resMap.put("id", id);
                 resMap.put("name", name);
                 resMap.put("description", description);
                 resMap.put("type", type);
                 resList.add(resMap);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        try {
            JsonNode rootNodeAgent = objectMapper.readTree(jsonAgent);
            JsonNode dataArr = rootNodeAgent.path("data");
            for (JsonNode node : dataArr) {
                Map<String, String> resMap = new HashMap<>();
                String id = node.path("id").asText();
                String name = node.path("title").asText();
                String description = node.path("description").asText();
                String type = "AGENT";
                resMap.put("id", id);
                resMap.put("name", name);
                resMap.put("description", description);
                resMap.put("type", type);
                resList.add(resMap);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        return Result.success(resList);
    }

    @Override
    public String findBySession(String sessionId) {
        Sessions sessions = chatMapper.findBySession(sessionId);
        return sessions.getAssistantId();
    }

    @Override
    public List<Assistant> list() {

        Map<String, Object> claims = ThreadLocalUtil.get();
        Integer role = (Integer) claims.get("role");

        return chatMapper.list(role);

    }


}
